package com.lisp.springframework.tx;

import java.util.Arrays;
import java.util.Date;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.lisp.springframework.tx.dao.InnerInvokeDao;
import com.lisp.springframework.tx.dao.JdbcTemplateDao;
import com.lisp.springframework.tx.dao.NamedJdbcTemplateDao;
import com.lisp.springframework.tx.model.Foo;
import com.lisp.springframework.tx.service.PropagationService;

@Configuration
@PropertySource(value="classpath:/app.properties")
@EnableTransactionManagement(mode = AdviceMode.PROXY)
@ComponentScan(basePackages = "com.lisp.springframework.tx")
public class TxApp {

	@Autowired
	private Environment env;
	
	// @Bean
	public DataSource dataSource() {
		return new DriverManagerDataSource(env.getProperty("mysql.url"), 
		        env.getProperty("mysql.username"), env.getProperty("mysql.password"));
	}
	
	@Bean
	public DataSource embededDataSource() {
	    return new EmbeddedDatabaseBuilder()
            .generateUniqueName(true)
            .setType(EmbeddedDatabaseType.DERBY)
            .setScriptEncoding("UTF-8")
            .ignoreFailedDrops(true)
            .addScript("classpath:sql/schema.sql")
            .addScript("classpath:sql/user_data.sql")
            .build();
	}
	
	@Bean
	public DataSourceTransactionManager txManager(DataSource dataSource) {
	    DataSourceTransactionManager txMgr = new DataSourceTransactionManager(dataSource);
	    // txMgr.setValidateExistingTransaction(true);
	    return txMgr;
	}
	
	public static void main(String[] args) {
        try(AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();) {            
            ctx.register(TxApp.class);
            ctx.refresh();
            
            testBasicOperation(ctx);
            
            try {
                PropagationService reqiredService = ctx.getBean(PropagationService.class);
                reqiredService.testPropagationRequired();
            } catch (Exception e) {
                System.out.println("TxApp Error: " + e.getMessage());
            }
            
            try {
                PropagationService reqiredService = ctx.getBean(PropagationService.class);
                reqiredService.testPropagationRequiredNew();
            } catch (Exception e) {
                System.out.println("TxApp Error: " + e.getMessage());
            }
            
            try {
                InnerInvokeDao innerInvokeDao = ctx.getBean(InnerInvokeDao.class);
                innerInvokeDao.testInnerInvoke();
            } catch (Exception e) {
                System.out.println("TxApp Error: " + e.getMessage());
            }
        }
    }

    static void testBasicOperation(AnnotationConfigApplicationContext ctx) {
        NamedJdbcTemplateDao fooDaoWithNamedParam = ctx.getBean(NamedJdbcTemplateDao.class);
        long id = System.currentTimeMillis();
        fooDaoWithNamedParam.insert(new Foo(id, 2, new Date()));
        System.out.println(fooDaoWithNamedParam.find(id));
        System.out.println(fooDaoWithNamedParam.findByIdIn(Arrays.asList(1L, id)));
        
        JdbcTemplateDao fooDao = ctx.getBean(JdbcTemplateDao.class);
        long id1 = System.currentTimeMillis();
        fooDao.insert(new Foo(id1, 2, new Date()));
        System.out.println(fooDao.find(id1));
        System.out.println(fooDao.findByIdIn(Arrays.asList(1L, id1)));
    }
}
